package pl.edu.pw.MidEmu.UI.Components;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.Dictionary;
import java.util.Hashtable;

import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
import javax.swing.filechooser.FileNameExtensionFilter;

import pl.edu.pw.MidEmu.Base.Components.BaseFrame;
import pl.edu.pw.MidEmu.Common.Consts;
import pl.edu.pw.MidEmu.Sensing.Processing.LoopProcessing;
import pl.edu.pw.MidEmu.Sensing.Processing.ProcessingAlgorithm;
import pl.edu.pw.MidEmu.Sensing.Processing.ValueProcessing;
import pl.edu.pw.MidEmu.Sensing.Processing.LoopProcessing.LoopSpecification;
import pl.edu.pw.MidEmu.Sensing.Processing.LoopProcessing.TimeSpecification;
import pl.edu.pw.MidEmu.Sensing.Processing.ValueProcessing.ValueSpecification;
import pl.edu.pw.MidEmu.Sensing.Sensors.ASensor;
import pl.edu.pw.MidEmu.Sensing.Sensors.GenericSensor;
import pl.edu.pw.MidEmu.Sensing.Sensors.HumiditySensor;
import pl.edu.pw.MidEmu.Sensing.Sensors.LuminancySensor;
import pl.edu.pw.MidEmu.Sensing.Sensors.PressureSensor;
import pl.edu.pw.MidEmu.Sensing.Sensors.TemperatureSensor;
import pl.edu.pw.MidEmu.Sensing.Values.GenericValue;
import pl.edu.pw.MidEmu.Sensing.Values.HumidityValue;
import pl.edu.pw.MidEmu.Sensing.Values.IValue;
import pl.edu.pw.MidEmu.Sensing.Values.LuminancyValue;
import pl.edu.pw.MidEmu.Sensing.Values.PressureValue;
import pl.edu.pw.MidEmu.Sensing.Values.TemperatureValue;
import pl.edu.pw.MidEmu.UI.Controllers.AddSensorController;
import pl.edu.pw.MidEmu.UI.Views.AddSensorView;

/**
 * Frame with GUI for adding new sensor
 * @author Mateusz Starodab
 *
 */
public class AddSensorFrame extends BaseFrame<JDialog, AddSensorController> implements AddSensorView
{
	private ButtonGroup bgInterval;
	private ButtonGroup bgLoop;

	private ButtonGroup bgValue;
	private JButton btnBrowse;
	private JButton btnOk;
	private JCheckBox cbxRange;
	private JFileChooser fchChooseFile;
	private JLabel lblFile;

	private JLabel lblInitialValue;
	private JLabel lblIntervalTime;
	private JLabel lblIntervalType;
	private JLabel lblIterationCount;
	private JLabel lblLoopType;
	private JLabel lblRange;

	private JLabel lblSensorType;

	private JLabel lblUnit;
	private JLabel lblValueType;
	private JComboBox lbxSensorTypes;
	private MidasComponent parent;
	private JRadioButton rbConstatnt;
	private JRadioButton rbContinous;
	private JRadioButton rbDecrement;
	private JRadioButton rbEndless;
	private JRadioButton rbExternal;
	private JRadioButton rbIncrement;
	private JRadioButton rbInterval;
	private JRadioButton rbIterational;
	private JRadioButton rbRandom;

	private JTextField tbxFile;
	private JTextField tbxHighRange;
	private JTextField tbxInitialValue;

	private JTextField tbxIntervalTime;
	private JTextField tbxIterationCount;

	private JTextField tbxLoRange;

	private JTextField tbxUnit;

	public AddSensorFrame(MidasComponent component)
	{
		AddSensorController controller = new AddSensorController();
		controller.setView(this);
		setController(controller);
		JDialog frame = new JDialog();
		parent = component;
		setComponent(frame);

		getComponent().setTitle(Consts.MainFrame_Title + " - " + parent.getName() + " - " + Consts.AddSensor_Title);
		getComponent().setSize(285, 515);
		getComponent().setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		getComponent().setModal(true);

		fchChooseFile = new JFileChooser();
		fchChooseFile.setMultiSelectionEnabled(false);
		fchChooseFile.setFileSelectionMode(JFileChooser.FILES_ONLY);
		fchChooseFile.setFileFilter(new FileNameExtensionFilter(Consts.AddSensor_FileChooser_CSVFilter, new String[] { Consts.AddSensor_FileChooser_CSVExtention, Consts.AddSensor_FileChooser_TxtExtention }));

		lblSensorType = new JLabel(Consts.AddSensor_DropLabel);
		lblSensorType.setLabelFor(lbxSensorTypes);
		DefaultComboBoxModel model = new DefaultComboBoxModel();
		model.addElement(new TemperatureSensor());
		model.addElement(new PressureSensor());
		model.addElement(new LuminancySensor());
		model.addElement(new HumiditySensor());
		model.addElement(new GenericSensor());
		lbxSensorTypes = new JComboBox(model);
		lbxSensorTypes.addActionListener(getController());
		lbxSensorTypes.setName(Consts.AddSensor_DropLabel);

		JPanel pnlMain = new JPanel(new BorderLayout());

		JPanel pnlContent = new JPanel();
		pnlContent.setLayout(new FlowLayout(FlowLayout.LEFT));

		JPanel pnlSensor = new JPanel();
		pnlSensor.setLayout(new FlowLayout(FlowLayout.LEFT));
		pnlSensor.setBorder(BorderFactory.createTitledBorder("Sensor"));
		pnlSensor.setPreferredSize(new Dimension(265, 105));

		pnlSensor.add(lblSensorType);
		pnlSensor.add(lbxSensorTypes);

		lblInitialValue = new JLabel(Consts.AddSensor_InitialValue);
		tbxInitialValue = new JTextField(14);
		lblInitialValue.setLabelFor(tbxInitialValue);
		pnlSensor.add(lblInitialValue);
		pnlSensor.add(tbxInitialValue);

		lblUnit = new JLabel(Consts.AddSensor_Unit);
		tbxUnit = new JTextField(18);
		tbxUnit.setName(Consts.AddSensor_Unit);
		lblUnit.setLabelFor(tbxUnit);

		pnlSensor.add(lblUnit);
		pnlSensor.add(tbxUnit);

		JPanel pnlLoop = new JPanel();
		pnlLoop.setLayout(new FlowLayout(FlowLayout.LEFT));
		pnlLoop.setBorder(BorderFactory.createTitledBorder("Loop"));
		pnlLoop.setPreferredSize(new Dimension(265, 140));

		lblLoopType = new JLabel(Consts.AddSensor_LoopType);
		bgLoop = new ButtonGroup();
		rbEndless = new JRadioButton(Consts.AddSensor_RadioEndless);
		rbEndless.addActionListener(getController());
		rbEndless.setName(Consts.AddSensor_RadioEndless);
		rbEndless.setSelected(true);
		bgLoop.add(rbEndless);
		rbIterational = new JRadioButton(Consts.AddSensor_RadioIterational);
		rbIterational.addActionListener(getController());
		rbIterational.setName(Consts.AddSensor_RadioIterational);
		bgLoop.add(rbIterational);

		pnlLoop.add(lblLoopType);
		pnlLoop.add(rbEndless);
		pnlLoop.add(rbIterational);

		lblIterationCount = new JLabel(Consts.AddSensor_IterationCount);
		tbxIterationCount = new JTextField(11);
		tbxIterationCount.setName(Consts.AddSensor_IterationCount);
		tbxIterationCount.setEnabled(false);
		lblIterationCount.setLabelFor(tbxIterationCount);

		pnlLoop.add(lblIterationCount);
		pnlLoop.add(tbxIterationCount);

		lblIntervalType = new JLabel(Consts.AddSensor_IntervalType);
		bgInterval = new ButtonGroup();
		rbContinous = new JRadioButton(Consts.AddSensor_RadioContinous);
		rbContinous.addActionListener(getController());
		rbContinous.setName(Consts.AddSensor_RadioContinous);
		rbContinous.setSelected(true);
		bgInterval.add(rbContinous);
		rbInterval = new JRadioButton(Consts.AddSensor_RadioInterval);
		rbInterval.addActionListener(getController());
		rbInterval.setName(Consts.AddSensor_RadioInterval);
		bgInterval.add(rbInterval);

		pnlLoop.add(lblIntervalType);
		pnlLoop.add(rbContinous);
		pnlLoop.add(rbInterval);

		lblIntervalTime = new JLabel(Consts.AddSensor_IntervalTime);
		tbxIntervalTime = new JTextField(10);
		tbxIntervalTime.setName(Consts.AddSensor_IntervalTime);
		tbxIntervalTime.setEnabled(false);
		lblIntervalTime.setLabelFor(tbxIntervalTime);

		pnlLoop.add(lblIntervalTime);
		pnlLoop.add(tbxIntervalTime);

		JPanel pnlValue = new JPanel();
		pnlValue.setLayout(new BorderLayout());
		pnlValue.setBorder(BorderFactory.createTitledBorder("Value"));
		pnlValue.setPreferredSize(new Dimension(265, 180));

		JPanel helperPanel = new JPanel(new BorderLayout());

		JPanel pnlValueType = new JPanel(new FlowLayout(FlowLayout.LEFT));
		lblValueType = new JLabel(Consts.AddSensor_ValueType);
		bgValue = new ButtonGroup();
		rbIncrement = new JRadioButton(Consts.AddSensor_RadioIncrement);
		rbIncrement.setName(Consts.AddSensor_RadioIncrement);
		rbIncrement.setSelected(true);
		rbIncrement.addActionListener(getController());
		bgValue.add(rbIncrement);
		rbDecrement = new JRadioButton(Consts.AddSensor_RadioDecrement);
		rbDecrement.setName(Consts.AddSensor_RadioDecrement);
		rbDecrement.addActionListener(getController());
		bgValue.add(rbDecrement);
		rbRandom = new JRadioButton(Consts.AddSensor_RadioRandom);
		rbRandom.setName(Consts.AddSensor_RadioRandom);
		rbRandom.addActionListener(getController());
		bgValue.add(rbRandom);
		rbConstatnt = new JRadioButton(Consts.AddSensor_RadioConstant);
		rbConstatnt.setName(Consts.AddSensor_RadioConstant);
		rbConstatnt.addActionListener(getController());
		bgValue.add(rbConstatnt);
		rbExternal = new JRadioButton(Consts.AddSensor_RadioExternal);
		rbExternal.setName(Consts.AddSensor_RadioExternal);
		rbExternal.addActionListener(getController());
		bgValue.add(rbExternal);

		pnlValueType.add(lblValueType);
		pnlValueType.add(rbIncrement);
		pnlValueType.add(rbDecrement);
		pnlValueType.add(rbRandom);
		pnlValueType.add(rbConstatnt);
		pnlValueType.add(rbExternal);

		helperPanel.add(pnlValueType, BorderLayout.CENTER);

		JPanel pnlRange = new JPanel(new FlowLayout(FlowLayout.LEFT));
		cbxRange = new JCheckBox(Consts.AddSensor_Range);
		cbxRange.addActionListener(getController());
		cbxRange.setName(Consts.AddSensor_Range);
		lblRange = new JLabel(Consts.AddSensor_Range);
		tbxLoRange = new JTextField(4);
		tbxLoRange.setEnabled(false);
		lblRange.setLabelFor(tbxLoRange);
		tbxHighRange = new JTextField(4);
		tbxHighRange.setEnabled(false);

		pnlRange.add(cbxRange);
		pnlRange.add(lblRange);
		pnlRange.add(tbxLoRange);
		pnlRange.add(tbxHighRange);

		helperPanel.add(pnlRange, BorderLayout.SOUTH);

		JPanel pnlFile = new JPanel(new FlowLayout(FlowLayout.LEFT));
		lblFile = new JLabel(Consts.AddSensor_FileLabel);
		tbxFile = new JTextField(10);
		tbxFile.setEditable(false);
		lblFile.setLabelFor(tbxFile);
		btnBrowse = new JButton(Consts.AddSensor_BrowseButton);
		btnBrowse.setName(Consts.AddSensor_BrowseButton);
		btnBrowse.addActionListener(getController());
		btnBrowse.setEnabled(false);

		pnlFile.add(lblFile);
		pnlFile.add(tbxFile);
		pnlFile.add(btnBrowse);

		pnlValue.add(helperPanel, BorderLayout.CENTER);
		pnlValue.add(pnlFile, BorderLayout.SOUTH);

		pnlContent.add(pnlSensor);
		pnlContent.add(pnlValue);
		pnlContent.add(pnlLoop);

		JPanel pnlSouth = new JPanel();
		pnlSouth.setLayout(new FlowLayout(FlowLayout.CENTER));

		btnOk = new JButton(Consts.AddSensor_OK);
		btnOk.setName(Consts.AddSensor_OK);
		btnOk.addActionListener(getController());
		pnlSouth.add(btnOk);

		pnlMain.add(pnlContent, BorderLayout.CENTER);
		pnlMain.add(pnlSouth, BorderLayout.SOUTH);

		getComponent().add(pnlMain);
	}

	@Override
	public void disableIntervalBox(boolean b)
	{
		tbxIntervalTime.setEnabled(!b);
	}

	@Override
	public void disableIterationBox(boolean b)
	{
		tbxIterationCount.setEnabled(!b);
	}

	@Override
	public void disableLoopBox(boolean b)
	{
		rbEndless.setEnabled(b);
		rbIterational.setEnabled(b);
		rbContinous.setEnabled(b);
		rbInterval.setEnabled(b);
		cbxRange.setEnabled(b);

		if (!b)
		{
			tbxIntervalTime.setEnabled(b);
			tbxIterationCount.setEnabled(b);
			rbEndless.setSelected(true);
			rbContinous.setSelected(true);
			tbxIntervalTime.setText("");
			tbxIterationCount.setText("");
			cbxRange.setSelected(false);
			disableRangeBox(false);
		}
		else
		{
			if (!rbContinous.getModel().isSelected())
				tbxIntervalTime.setEnabled(b);
			if (!rbEndless.getModel().isSelected())
				tbxIterationCount.setEnabled(b);
		}
	}

	@Override
	public void disableRangeBox(boolean selected)
	{
		tbxHighRange.setEnabled(selected);
		tbxLoRange.setEnabled(selected);
	}

	@Override
	public void enableExternalFile(boolean b)
	{
		btnBrowse.setEnabled(b);
		tbxUnit.setEnabled(!b);
		tbxInitialValue.setEnabled(!b);

		if (!b)
		{
			tbxFile.setText("");
		}
		else
		{
			tbxUnit.setText("");
			tbxInitialValue.setText("");
		}
	}

	private Dictionary<Integer, String> parseFile(String text)
	{
		Hashtable<Integer, String> values = new Hashtable<Integer, String>();
		try
		{
			FileReader reader = new FileReader(text);
			BufferedReader br = new BufferedReader(reader);
			String line;
			while ((line = br.readLine()) != null)
			{
				int indexOfSemicolon = line.indexOf(";");
				values.put(Integer.valueOf(line.substring(0, indexOfSemicolon)), line.substring(indexOfSemicolon + 1));
			}
			br.close();
			reader.close();
		}
		catch (Exception ex)
		{
			ex.printStackTrace();
		}
		return values;
	}

	@Override
	public void reportValidationErrors()
	{
		JOptionPane.showMessageDialog(getComponent(), "Sensor configuration is invalid.\nMake sure you fill all active fields", "Invalid configuration", JOptionPane.ERROR_MESSAGE);
	}

	@Override
	public void setSensor()
	{
		LoopProcessing loopProcessing = new LoopProcessing();

		if (rbEndless.getModel().isSelected())
			loopProcessing.setLoopSpecification(LoopSpecification.Endless);
		else
		{
			loopProcessing.setLoopSpecification(LoopSpecification.Incremental);
			loopProcessing.setIterationCount(Integer.parseInt(tbxIterationCount.getText()));
		}

		if (rbContinous.getModel().isSelected() && !rbConstatnt.getModel().isSelected() && !rbExternal.getModel().isSelected())
			loopProcessing.setTimeSpecification(TimeSpecification.Contionus);
		else
		{
			loopProcessing.setTimeSpecification(TimeSpecification.Interval);
			if (rbConstatnt.getModel().isSelected())
				loopProcessing.setIntervalTime(10000);
			else if (rbExternal.getModel().isSelected())
				loopProcessing.setIntervalTime(1000);
			else
				loopProcessing.setIntervalTime(Integer.parseInt(tbxIntervalTime.getText()));
		}

		ValueProcessing valueProcessing = new ValueProcessing();
		if (rbIncrement.getModel().isSelected())
		{
			if (cbxRange.getModel().isSelected())
			{
				valueProcessing.setValueSpecification(ValueSpecification.RangeIncrement);
			}
			else
			{
				valueProcessing.setValueSpecification(ValueSpecification.Increment);
			}
		}
		else if (rbDecrement.getModel().isSelected())
		{
			if (cbxRange.getModel().isSelected())
			{
				valueProcessing.setValueSpecification(ValueSpecification.RangeDecrement);
			}
			else
			{
				valueProcessing.setValueSpecification(ValueSpecification.Decrement);
			}
		}
		else if (rbRandom.getModel().isSelected())
		{
			if (cbxRange.getModel().isSelected())
			{
				valueProcessing.setValueSpecification(ValueSpecification.RangeRandom);
			}
			else
			{
				valueProcessing.setValueSpecification(ValueSpecification.Random);
			}
		}
		else if (rbExternal.getModel().isSelected())
		{
			valueProcessing.setValueSpecification(ValueSpecification.External);
			Dictionary<Integer, String> externalValues = parseFile(tbxFile.getText());
			valueProcessing.setExternalValues(externalValues);
		}

		boolean getRangeValues = cbxRange.getModel().isSelected();

		String loText = tbxLoRange.getText();
		String hiText = tbxHighRange.getText();
		String unit = tbxUnit.getText();
		String initialText = tbxInitialValue.getText();
		if (rbExternal.getModel().isSelected())
			initialText = "1";
		IValue loValue = null;
		IValue hiValue = null;
		IValue initialValue = null;
		ASensor sensor = (ASensor) lbxSensorTypes.getModel().getSelectedItem();
		if (sensor instanceof TemperatureSensor)
		{
			if (getRangeValues)
			{
				loValue = new TemperatureValue(Integer.parseInt(loText), unit);
				hiValue = new TemperatureValue(Integer.parseInt(hiText), unit);
			}
			initialValue = new TemperatureValue(Integer.parseInt(initialText), unit);
		}
		else if (sensor instanceof GenericSensor)
		{
			if (getRangeValues)
			{
				loValue = new GenericValue(Integer.parseInt(loText), unit);
				hiValue = new GenericValue(Integer.parseInt(hiText), unit);
			}
			initialValue = new GenericValue(Integer.parseInt(initialText), unit);
		}
		else if (sensor instanceof LuminancySensor)
		{
			if (getRangeValues)
			{
				loValue = new LuminancyValue(Integer.parseInt(loText), unit);
				hiValue = new LuminancyValue(Integer.parseInt(hiText), unit);
			}
			initialValue = new LuminancyValue(Integer.parseInt(initialText), unit);
		}
		else if (sensor instanceof PressureSensor)
		{
			if (getRangeValues)
			{
				loValue = new PressureValue(Integer.parseInt(loText), unit);
				hiValue = new PressureValue(Integer.parseInt(hiText), unit);
			}
			initialValue = new PressureValue(Integer.parseInt(initialText), unit);
		}
		else if (sensor instanceof HumiditySensor)
		{
			if (getRangeValues)
			{
				loValue = new HumidityValue(Integer.parseInt(loText), unit);
				hiValue = new HumidityValue(Integer.parseInt(hiText), unit);
			}
			initialValue = new HumidityValue(Integer.parseInt(initialText), unit);
		}

		valueProcessing.setLowLimit(loValue);
		valueProcessing.setHighLimit(hiValue);

		ProcessingAlgorithm algorithm = new ProcessingAlgorithm(loopProcessing, valueProcessing);

		sensor.setInitialReading(initialValue);

		sensor.setProcessingAlgorithm(algorithm);

		parent.getController().addSensor(sensor);

		getComponent().setVisible(false);
		getComponent().dispose();
	}

	@Override
	public void showFileDialog()
	{
		fchChooseFile.showOpenDialog(getComponent());
		File selectedFile = fchChooseFile.getSelectedFile();
		if (selectedFile != null)
		{
			String _filePath = selectedFile.getAbsolutePath();

			tbxFile.setText(_filePath);
		}
	}

	@Override
	public boolean validate()
	{
		if (tbxIterationCount.isEnabled())
		{
			try
			{
				Integer.parseInt(tbxIterationCount.getText());
			}
			catch (Exception ex)
			{
				return false;
			}
		}
		if (tbxIntervalTime.isEnabled())
		{
			try
			{
				Integer.parseInt(tbxIntervalTime.getText());
			}
			catch (Exception ex)
			{
				return false;
			}
		}
		if (tbxLoRange.isEnabled())
		{
			if (tbxLoRange.getText().trim().equals(""))
				return false;
		}
		if (tbxHighRange.isEnabled())
		{
			if (tbxHighRange.getText().trim().equals(""))
				return false;
		}
		if (tbxInitialValue.isEnabled())
		{
			if (tbxInitialValue.getText().trim().equals(""))
				return false;
		}
		if (btnBrowse.isEnabled())
		{
			if (tbxFile.getText().trim().equals(""))
				return false;
		}
		if (tbxUnit.isEnabled())
		{
			if (tbxUnit.getText().trim().equals(""))
				return false;
		}

		return true;
	}
}
